package com.aps.common.model.genetic;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * @author jsy
 * @description:机器设备
 * @date 2025/9/12 09:46
 */
@Schema(description = "机器设备")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Equipment {
    /**
     * 主键
     */
    private String id;
    /**
     * 设备唯一标序列号
     */
    private String serialNumber;
    /**
     * 设备类型
     */
    private String type;
    /**
     * 设备下一次可用时间（最早可以开始新工序的时间）
     */
    private LocalDateTime nextAvailableTime;

    /**
     * 设备已安排的工序时间段列表，用于冲突检测和利用率计算
     */
    private List<ProcessInterval> equipmentIntervals;

    public Equipment(String id, String serialNumber, String type, LocalDateTime nextAvailableTime) {
        this.id = id;
        this.serialNumber = serialNumber;
        this.type = type;
        this.nextAvailableTime = nextAvailableTime;
        equipmentIntervals = new ArrayList<>();
    }

    /**
     * 添加时间区间到设备的已安排列表中
     * <p>
     * 该方法会：
     * 1. 将新的时间区间添加到已安排列表
     * 2. 保持列表按开始时间排序
     * 3. 更新设备的下一次可用时间
     *
     * @param start 开始时间
     * @param end   结束时间
     */
    public void addEquipmentInterval(String processId, LocalDateTime start, LocalDateTime end, int productIndex, String machineId) {
        if (start.isAfter(end)) {
            throw new IllegalArgumentException("开始时间不能晚于结束时间");
        }

        // 添加新的时间区间
        equipmentIntervals.add(new ProcessInterval(processId, start, end, productIndex, machineId));

        // 保持区间有序，便于冲突检测和利用率计算
        equipmentIntervals.sort(Comparator.comparing(ProcessInterval::getStartTime));

        // 更新下一次可用时间：取所有已安排区间结束时间的最大值
        LocalDateTime maxEndTime = equipmentIntervals.stream()
                .map(ProcessInterval::getEndTime)
                .max(LocalDateTime::compareTo)
                .orElse(nextAvailableTime);

        if (maxEndTime.isAfter(nextAvailableTime)) {
            nextAvailableTime = maxEndTime;
        }
    }

    /**
     * @description: 重置设备可用时间、设备已安排的工序时间段列表，用于冲突检测和利用率计算
     * @author: jsy
     * @time: 2025/10/4 15:11
     */
    public void resetAvailability(LocalDateTime startTime) {
        this.nextAvailableTime = startTime;
        this.clearIntervals();
    }

    /**
     * @description: 清除所有已安排的时间区间
     * @author: jsy
     * @time: 2025/10/4 15:11
     */
    public void clearIntervals() {
        equipmentIntervals.clear();
    }

    /**
     * @description:  检查指定时间区间是否与已有安排冲突
     * 冲突检测逻辑：
     *      * - 如果新区间的结束时间早于某个已安排区间的开始时间，则无冲突
     *      * - 如果新区间的开始时间晚于某个已安排区间的结束时间，则无冲突
     *      * - 其他情况都存在冲突
     * @author: jsy
     * @time: 2025/10/14 11:12
     */
    public boolean hasConflict(LocalDateTime start, LocalDateTime end) {
        if (start.isAfter(end)) {
            throw new IllegalArgumentException("开始时间不能晚于结束时间");
        }

        return equipmentIntervals.stream()
                .anyMatch(interval ->
                        end.isAfter(interval.getStartTime()) && start.isBefore(interval.getEndTime())
                );
    }
}
